package com.google.bitcoin.core;

import com.google.common.base.Preconditions;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;

/**
 * @author apetersson
 */
public class PubKey implements OutputDestination {

    public static final int KEY_LENGTH = 65;
    private final byte[] data;

    private PubKey(byte[] data) { //todo great candicate for interning
        Preconditions.checkArgument(data.length == KEY_LENGTH);
        this.data = new byte[KEY_LENGTH];
        System.arraycopy(data, 0, this.data, 0, KEY_LENGTH);
    }

    private static final PubKey EMPTY_PUBKEY = new PubKey(new byte[KEY_LENGTH]);

    public static PubKey valueOf(byte[] data) {
        if (Arrays.equals(Block.EMPTY_BYTES, data)) {
            return EMPTY_PUBKEY;
        }
        return new PubKey(data);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PubKey pubKey = (PubKey) o;
        //basically same as Arrays.equals but without length and instance checks
        for (int i = 0; i < KEY_LENGTH; i++) {
            if (data[i] != pubKey.data[i]) {
                return false;
            }
        }
        return true;
    }

    public Address toAddress(NetworkParameters params) {
        return new Address(params, Utils.sha256hash160(data)); //cache as transient / weakhashmap?
    }

    @Override
    public int hashCode() {
        return Arrays.hashCode(data);
    }

    @Override
    public String toString() {
        return Utils.bytesToHexString(data);
    }

    /**
     * use only in case of emergency, like when writing to the wire/disk
     *
     * @return the raw bytes of the pubkey. these should never be modified, it is not copied in defense.
     */
    public byte[] getBytes() {
        return data;
    }

    public byte[] createOutputScript() {
        try {
            // TODO: Do this by creating a Script *first* then having the script reassemble itself into bytes.
            ByteArrayOutputStream bits = new UnsafeByteArrayOutputStream(KEY_LENGTH + 1);
            Opcodes.writeBytes(bits, data);
            bits.write(Opcodes.OP_CHECKSIG);
            return bits.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException(e);  // Cannot happen.
        }
    }
}
